// /////////////////////////////////////////////////////////////////////////////
//
// Name:            DataStack.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           DataStacks provide an interface for storing (and removing)
//                  variables that can be accessed from anywhere within the
//                  framework. Furthermore it's also possible to save these
//                  variables into files.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files.
#include "DataStack.h"
#include "GameManager.h"


// DataStack class
// Static variables.
u32 DataStack::mIDCount = 0;

// DataStack constructor.
DataStack::DataStack(const std::string &name)
: mName(name)
{
    mID = mIDCount++;
}

// DataStack deconstructor.
DataStack::~DataStack()
{
    clear();
}

// Initialises the DataStack.
void DataStack::init()
{
}

// Clears the DataStack.
void DataStack::clear()
{
    removeVars();
}

// Gets the ID of the DataStack.
u32 DataStack::getID() const
{
    return mID;
}

// Gets the name of the DataStack.
const std::string& DataStack::getName() const
{
    return mName;
}

// Gets the number of variables on the DataStack.
u32 DataStack::getSize() const
{
    return mVars.size();
}

// Try to find the given variable.
std::string DataStack::getVar(const std::string &name)
{
    // Try to find the given variable.
    std::map<std::string, std::string>::iterator it;
    it = mVars.find(name);

    // Did we find the variable?
    if(it != mVars.end())
      return it->second;

    // We didn't find the variable.
    else return NULL;
}

// Removes all variables from this DataStack.
void DataStack::removeVars()
{
    mVars.clear();
}

// Removes the variable with the given name.
bool DataStack::removeVar(const std::string &name)
{
    std::map<std::string, std::string>::iterator it;
    it = mVars.find(name);

    if(it != mVars.end())
    {
       mVars.erase(it);
       return true;
    }

    else return false;
}

// Saves the Data Stack as a bencoded file.
bool DataStack::saveBencode(const std::string &fileName)
{
    // Get pointer to the File System of Irrlicht.
    IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

    // Open the file.
    IWriteFile *file = fileSystem->createAndWriteFile(fileName.c_str());

    // Create a buffer.
    std::stringstream fBuffer;

    // Write the header to the buffer.
    fBuffer << "d3:fidi2419e5:ftype3:bdse";

    // Write the contents of the data stack to the buffer.
    fBuffer << "d";

    std::map<std::string, std::string>::iterator it;
    for(it = mVars.begin(); it != mVars.end(); it++)
    {
        fBuffer << it->first.size() << ":" << it->first;
        fBuffer << it->second.size() << ":" << it->second;
    }

    fBuffer << "e";

    // Write to the file.
    file->write(fBuffer.str().c_str(), fBuffer.str().size());

    // Close the file.
    file->drop();

    return true;
}

// Loads the Data Stack from a bencoded file.
bool DataStack::loadBencode(const std::string &fileName)
{
    // Get pointer to the File System of Irrlicht.
    IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

    // Does the file exist?
    if(!fileSystem->existFile(fileName.c_str()))
      return false;

    // Open the given file and load the contents to the buffer.
    IReadFile *file = fileSystem->createAndOpenFile(fileName.c_str());

    unsigned long fSize = file->getSize();
    c8 *fBuffer = new c8[fSize+1];
    file->read(fBuffer, fSize);
    file->drop();

    // Indentify the file as Bencoded Data Stack.
    std::string fHeader = std::string(fBuffer).substr(0, 25);

    if(fHeader != "d3:fidi2419e5:ftype3:bdse")
      return false;

    // Delete any previous data.
    removeVars();

    // Get remaining data from the file.
    std::stringstream bCountStream;
    unsigned long bCount = 0;

    s32 state = 0;
    std::string key;
    std::string value;

    for(unsigned long i = 25; i < fSize; i++)
    {
        if(bCount != 0)
        {
           if(state == 0)
             key += fBuffer[i];

           else if(state == 1)
             value += fBuffer[i];

           bCount--;
        }

        else
        {
           if(!key.empty())
             state = 1;

           if(!value.empty())
           {
              mVars[key] = value;
              key.clear();
              value.clear();
              state = 0;
           }

           if(fBuffer[i] >= '0' && fBuffer[i] <= '9')
             bCountStream << fBuffer[i];
        }

        if(fBuffer[i] == ':')
        {
            bCountStream >> bCount;
            bCountStream.clear();
        }
    }

    // Remove the buffer.
    delete[] fBuffer;

    // We're done.
    return true;
}

// Saves the Data Stack as a XML file.
bool DataStack::saveXML(const std::string &fileName)
{
    // Get pointer to the File System of Irrlicht.
    IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

    // Open the file.
    IWriteFile *file = fileSystem->createAndWriteFile(fileName.c_str());

    // Create a buffer.
    std::stringstream fBuffer;

    // Write the header to the buffer.
    fBuffer << "<?xml version=\"1.0\"?>\n";

    // Write the contents of the data stack to the buffer.
    fBuffer << "<datastack>\n";

    std::map<std::string, std::string>::iterator it;
    for(it = mVars.begin(); it != mVars.end(); it++)
      fBuffer << "   <var name=\"" << it->first << "\" value=\"" << it->second << "\" />\n";

    fBuffer << "</datastack>";

    // Write to the file.
    file->write(fBuffer.str().c_str(), fBuffer.str().size());

    // Close the file.
    file->drop();

    return true;
}

// Loads the Data Stack from a XML file.
bool DataStack::loadXML(const std::string &fileName)
{
    // Get pointer to the File System of Irrlicht.
    IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

    // Does the file exist?
    if(!fileSystem->existFile(fileName.c_str()))
      return false;

    // Open the file.
    IXMLReader *file = fileSystem->createXMLReader(fileName.c_str());

    if(!file)
      return false;

    // Read from the file.
    bool isDataStack = false;

    while(file && file->read())
    {
       switch(file->getNodeType())
       {
          case io::EXN_ELEMENT:

             // Did we find a <datastack>?
             if(stringw("datastack") == file->getNodeName())
               isDataStack = true;

             // Did we find a <var>?
             if(isDataStack)
             {
                if(stringw("var") == file->getNodeName())
                {
                   stringc name = file->getAttributeValue(L"name");
                   stringc value = file->getAttributeValue(L"value");
                   mVars[name.c_str()] = value.c_str();
                }
             }

             break;

         default:
            break;
        }
    }

    if(!isDataStack)
      return false;

    // Close the file.
    file->drop();

    return true;
}

// End of File
